"""
OKX交易所客户端封装
"""
import ccxt
import time
from typing import Dict, List, Optional, Any
from datetime import datetime
from loguru import logger

from .rate_limiter import RateLimiter


class OKXClient:
    """OKX交易所客户端"""
    
    def __init__(self, config: Dict):
        """
        初始化OKX客户端
        
        Args:
            config: 配置字典
        """
        self.config = config
        self.exchange: Optional[ccxt.okx] = None
        self.rate_limiter: Optional[RateLimiter] = None
        self.connected = False
        
        # 重试配置
        self.max_retries = config.get('retry', {}).get('max_attempts', 3)
        self.retry_delay = config.get('retry', {}).get('delay', 1)
        self.retry_backoff = config.get('retry', {}).get('backoff', 2)
    
    def connect(self) -> bool:
        """
        连接到OKX交易所
        
        Returns:
            是否连接成功
        """
        try:
            # 初始化CCXT客户端
            self.exchange = ccxt.okx({
                'apiKey': self.config.get('api_key', ''),
                'secret': self.config.get('secret_key', ''),
                'password': self.config.get('passphrase', ''),
                'enableRateLimit': False,  # 使用自定义限流器
                'timeout': self.config.get('timeout', {}).get('default', 5) * 1000,
            })
            
            # 设置沙盒模式
            if self.config.get('sandbox', True):
                self.exchange.set_sandbox_mode(True)
                logger.info("OKX客户端已设置为模拟盘模式")
            
            # 初始化限流器
            rate_limit_config = self.config.get('rate_limit', {})
            if rate_limit_config.get('enabled', True):
                self.rate_limiter = RateLimiter(
                    requests_per_second=rate_limit_config.get('requests_per_second', 10),
                    burst=rate_limit_config.get('burst', 20)
                )
                logger.info("API限流器已启用")
            
            # 测试连接
            self.exchange.load_markets()
            self.connected = True
            logger.info("OKX交易所连接成功")
            return True
            
        except Exception as e:
            logger.error(f"连接OKX交易所失败: {e}")
            self.connected = False
            return False
    
    def disconnect(self):
        """断开连接"""
        self.connected = False
        self.exchange = None
        logger.info("已断开OKX交易所连接")
    
    def _execute_with_retry(self, func, *args, **kwargs) -> Any:
        """
        执行带重试机制的请求
        
        Args:
            func: 要执行的函数
            *args: 位置参数
            **kwargs: 关键字参数
            
        Returns:
            函数执行结果
        """
        # 获取请求许可
        if self.rate_limiter:
            self.rate_limiter.acquire(timeout=30)
        
        last_exception = None
        delay = self.retry_delay
        
        for attempt in range(self.max_retries):
            try:
                result = func(*args, **kwargs)
                if attempt > 0:
                    logger.info(f"请求在第{attempt + 1}次尝试后成功")
                return result
                
            except (ccxt.NetworkError, ccxt.RequestTimeout) as e:
                last_exception = e
                if attempt < self.max_retries - 1:
                    logger.warning(f"网络错误，{delay}秒后重试 (尝试 {attempt + 1}/{self.max_retries}): {e}")
                    time.sleep(delay)
                    delay *= self.retry_backoff
                else:
                    logger.error(f"请求失败，已达到最大重试次数: {e}")
                    
            except ccxt.ExchangeError as e:
                logger.error(f"交易所错误: {e}")
                raise
                
            except Exception as e:
                logger.error(f"未知错误: {e}")
                raise
        
        raise last_exception
    
    def get_balance(self) -> Dict:
        """
        查询账户余额
        
        Returns:
            余额信息字典
        """
        if not self.connected:
            raise RuntimeError("未连接到交易所")
        
        try:
            balance = self._execute_with_retry(self.exchange.fetch_balance)
            logger.debug(f"查询余额成功")
            return balance
        except Exception as e:
            logger.error(f"查询余额失败: {e}")
            raise
    
    def get_positions(self, symbol: Optional[str] = None) -> List[Dict]:
        """
        查询持仓
        
        Args:
            symbol: 交易对，None表示查询所有持仓
            
        Returns:
            持仓列表
        """
        if not self.connected:
            raise RuntimeError("未连接到交易所")
        
        try:
            positions = self._execute_with_retry(self.exchange.fetch_positions, symbol)
            # 过滤掉空持仓
            active_positions = [p for p in positions if float(p.get('contracts', 0)) > 0]
            logger.debug(f"查询持仓成功，活跃持仓数: {len(active_positions)}")
            return active_positions
        except Exception as e:
            logger.error(f"查询持仓失败: {e}")
            raise
    
    def get_orders(self, symbol: str, status: Optional[str] = None) -> List[Dict]:
        """
        查询订单
        
        Args:
            symbol: 交易对
            status: 订单状态 (open/closed)，None表示查询所有
            
        Returns:
            订单列表
        """
        if not self.connected:
            raise RuntimeError("未连接到交易所")
        
        try:
            if status == 'open':
                orders = self._execute_with_retry(self.exchange.fetch_open_orders, symbol)
            elif status == 'closed':
                orders = self._execute_with_retry(self.exchange.fetch_closed_orders, symbol)
            else:
                orders = self._execute_with_retry(self.exchange.fetch_orders, symbol)
            
            logger.debug(f"查询订单成功，订单数: {len(orders)}")
            return orders
        except Exception as e:
            logger.error(f"查询订单失败: {e}")
            raise
    
    def create_order(self, symbol: str, side: str, order_type: str, 
                    amount: float, price: Optional[float] = None, 
                    params: Optional[Dict] = None) -> Dict:
        """
        创建订单（开仓）
        
        Args:
            symbol: 交易对，如 'BTC/USDT:USDT'
            side: 订单方向 'buy' 或 'sell'
            order_type: 订单类型 'market' 或 'limit'
            amount: 订单数量（合约张数）
            price: 限价单价格，市价单可为None
            params: 额外参数，如 {'tdMode': 'cross', 'posSide': 'long'}
            
        Returns:
            订单信息字典
        """
        if not self.connected:
            raise RuntimeError("未连接到交易所")
        
        try:
            # 验证参数
            if side not in ['buy', 'sell']:
                raise ValueError(f"无效的订单方向: {side}")
            
            if order_type not in ['market', 'limit']:
                raise ValueError(f"无效的订单类型: {order_type}")
            
            if order_type == 'limit' and price is None:
                raise ValueError("限价单必须指定价格")
            
            if amount <= 0:
                raise ValueError(f"订单数量必须大于0: {amount}")
            
            # 设置默认参数
            if params is None:
                params = {}
            
            # 确保设置交易模式（全仓/逐仓）
            if 'tdMode' not in params:
                params['tdMode'] = 'cross'  # 默认全仓
            
            # 创建订单
            logger.info(f"创建订单: {symbol} {side} {order_type} 数量={amount} 价格={price}")
            order = self._execute_with_retry(
                self.exchange.create_order,
                symbol=symbol,
                type=order_type,
                side=side,
                amount=amount,
                price=price,
                params=params
            )
            
            logger.info(f"订单创建成功: ID={order.get('id')}, 状态={order.get('status')}")
            return order
            
        except Exception as e:
            logger.error(f"创建订单失败: {e}")
            raise
    
    def cancel_order(self, order_id: str, symbol: str) -> bool:
        """
        撤销订单
        
        Args:
            order_id: 订单ID
            symbol: 交易对
            
        Returns:
            是否撤销成功
        """
        if not self.connected:
            raise RuntimeError("未连接到交易所")
        
        try:
            logger.info(f"撤销订单: ID={order_id}, 交易对={symbol}")
            result = self._execute_with_retry(
                self.exchange.cancel_order,
                id=order_id,
                symbol=symbol
            )
            
            logger.info(f"订单撤销成功: ID={order_id}")
            return True
            
        except ccxt.OrderNotFound as e:
            logger.warning(f"订单不存在或已撤销: {order_id}")
            return False
        except Exception as e:
            logger.error(f"撤销订单失败: {e}")
            raise
    
    def close_position(self, symbol: str, side: str, amount: Optional[float] = None,
                      params: Optional[Dict] = None) -> Dict:
        """
        平仓
        
        Args:
            symbol: 交易对
            side: 持仓方向 'long' 或 'short'
            amount: 平仓数量，None表示全部平仓
            params: 额外参数
            
        Returns:
            平仓订单信息
        """
        if not self.connected:
            raise RuntimeError("未连接到交易所")
        
        try:
            # 验证参数
            if side not in ['long', 'short']:
                raise ValueError(f"无效的持仓方向: {side}")
            
            # 查询当前持仓
            positions = self.get_positions(symbol)
            target_position = None
            
            for pos in positions:
                pos_side = pos.get('side', '').lower()
                if pos_side == side:
                    target_position = pos
                    break
            
            if not target_position:
                logger.warning(f"未找到持仓: {symbol} {side}")
                raise ValueError(f"未找到持仓: {symbol} {side}")
            
            # 确定平仓数量
            position_amount = abs(float(target_position.get('contracts', 0)))
            close_amount = amount if amount is not None else position_amount
            
            if close_amount > position_amount:
                logger.warning(f"平仓数量({close_amount})超过持仓数量({position_amount})，调整为持仓数量")
                close_amount = position_amount
            
            # 确定平仓方向（与持仓方向相反）
            close_side = 'sell' if side == 'long' else 'buy'
            
            # 设置平仓参数
            if params is None:
                params = {}
            
            params['tdMode'] = target_position.get('marginMode', 'cross')
            params['posSide'] = side
            params['reduceOnly'] = True  # 只减仓
            
            # 执行平仓（使用市价单）
            logger.info(f"平仓: {symbol} {side} 数量={close_amount}")
            order = self.create_order(
                symbol=symbol,
                side=close_side,
                order_type='market',
                amount=close_amount,
                params=params
            )
            
            logger.info(f"平仓成功: ID={order.get('id')}")
            return order
            
        except Exception as e:
            logger.error(f"平仓失败: {e}")
            raise
